//
//  MusicKitPluginPlayer.swift
//  music_kit
//
//  Created by Longjiang Lv on 2022/5/26.
//

import Foundation
import MusicKit
import Combine

#if os(iOS)
import Flutter
#else
import FlutterMacOS
#endif

let kErrorPlay = "ERR_PLAYER_PLAY"
let kErrorPrepareToPlay = "ERR_PLAYER_PREPARE_TO_PLAY"
let kErrorSkipToNext = "ERR_PLAYER_SKIP_TO_NEXT"
let kErrorSkipToPrevious = "ERR_PLAYER_SKIP_TO_PREVIOUS"

extension MusicKitPlugin {
    func isPreparedToPlay(_ result: @escaping FlutterResult) {
        result(musicPlayer.isPreparedToPlay)
    }
    
    func playbackTime(_ result: @escaping FlutterResult) {
        result(musicPlayer.playbackTime)
    }
    
    func setPlaybackTime(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
        guard let args = call.arguments as? [String: Any],
              let time = args["time"] as? Double
        else {
            result(FlutterError(code: "INVALID_ARGUMENT", message: "Missing time argument", details: nil))
            return
        }
        
        musicPlayer.playbackTime = time
        result(nil)
    }
    
    func musicPlayerState(_ result: @escaping FlutterResult) {
        result(musicPlayer.state.jsonObject())
    }
    
    func pause(_ result: @escaping FlutterResult) {
        musicPlayer.pause()
        result(nil)
    }
    
    func play(_ result: @escaping FlutterResult) {
        Task {
            do {
                try await musicPlayer.play()
                result(nil)
            } catch {
                result(FlutterError(code: kErrorPlay, message: error.localizedDescription))
            }
        }
    }
    
    func prepareToPlay(_ result: @escaping FlutterResult) {
        Task {
            do {
                try await musicPlayer.prepareToPlay()
                result(nil)
            } catch {
                // 这个错误不影响
                result(nil)
            }
        }
    }
    
    func restartCurrentEntry(_ result: @escaping FlutterResult) {
        musicPlayer.restartCurrentEntry()
        result(nil)
    }
    
    func skipToNextEntry(_ result: @escaping FlutterResult) {
        Task {
            do {
                try await musicPlayer.skipToNextEntry()
                result(nil)
            } catch {
                result(FlutterError(code: kErrorSkipToNext, message: error.localizedDescription))
            }
        }
    }
    
    func skipToPreviousEntry(_ result: @escaping FlutterResult) {
        Task {
            do {
                try await musicPlayer.skipToPreviousEntry()
                result(nil)
            } catch {
                result(FlutterError(code: kErrorSkipToPrevious, message: error.localizedDescription))
            }
        }
    }
    
    
    func skipToEntry(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
        guard let args = call.arguments as? [String: Any],
              let index = args["index"] as? Int
        else {
            result(FlutterError(code: "INVALID_ARGUMENT", message: "Missing index argument", details: nil))
            return
        }
        
        
    }
    
    func isSongExistLibrary(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
        guard let args = call.arguments as? [String: Any],
              let songId = args["songId"] as? String
        else {
            result(FlutterError(code: "INVALID_ARGUMENT", message: "Missing songId argument", details: nil))
            return
        }
        
        Task {
            do {
                
                if #available(iOS 16.0, *) {
                    var request = MusicLibraryRequest<Song>()
                    request.filter(matching: \.id, equalTo: MusicItemID(songId))

                    let response = try await request.response()
                    print("结果是 \(!(response.items.isEmpty))")
                    result(!(response.items.isEmpty))
                } else {
                    /// 其他版本暂时不管
                    result(false)
                }

            } catch {
                print("报错了 \(error.localizedDescription)")
                result(nil)
            }
        }
        
    }
    
    func addSongToLibrary(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
        guard let args = call.arguments as? [String: Any],
              let songId = args["songId"] as? String
        else {
            result(FlutterError(code: "INVALID_ARGUMENT", message: "Missing songId argument", details: nil))
            return
        }
        
        Task {
            do {
                
                if #available(iOS 16.0, *) {
                    let id = MusicItemID(songId)
                    
                    // 1. 获取 Apple Music Catalog 中的歌曲
                    let request = MusicCatalogResourceRequest<Song>(matching: \.id, equalTo: id)
                    let response = try await request.response()
                    
                    guard let song = response.items.first else {
                        throw NSError(domain: "SongNotFound", code: 0, userInfo: nil)
                    }
                    
                    // 2. 添加到资料库（仅 iOS 16+）
                    try await MusicLibrary.shared.add(song)
                    result(nil)
                } else {
                    /// 其他版本暂时不管
                    result(nil)
                }

            } catch {
                print("报错了 \(error.localizedDescription)")
                result(nil)
            }
        }
        
    }
    
    func removeSongToLibrary(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
        guard let args = call.arguments as? [String: Any],
              let songId = args["songId"] as? String
        else {
            result(FlutterError(code: "INVALID_ARGUMENT", message: "Missing songId argument", details: nil))
            return
        }
        
        Task {
            do {
                
                if #available(iOS 16.0, *) {
                    let id = MusicItemID(songId)
                    
                    // 1. 查找资料库中是否已有该歌曲（不能用 catalog 查）
                    var request = MusicLibraryRequest<Song>()
                    request.filter(matching: \.id, equalTo: id)
                    
                    let response = try await request.response()
                    
                    guard let song = response.items.first else {
                        throw NSError(domain: "NotInLibrary", code: 0, userInfo: [NSLocalizedDescriptionKey: "歌曲不在资料库中"])
                    }
                    
                    // 2. 删除
//                    try await MusicLibrary.shared.delete(song)
                    result(nil)
                } else {
                    /// 其他版本暂时不管
                    result(nil)
                }

            } catch {
                print("报错了 \(error.localizedDescription)")
                result(nil)
            }
        }
        
    }
    
    func stop(_ result: @escaping FlutterResult) {
        musicPlayer.stop()
        result(nil)
    }
    
}

enum MusicItemType {
  case album
  case artist
  case curator
  case genre
  case musicVideo
  case playlist
  case radioShow
  case recordLabel
  case song
  case station
  case track
  
  init?(_ value: String) {
    switch value.lowercased() {
    case "albums": self = .album
    case "artists": self = .artist
    case "curators": self = .curator
    case "genres": self = .genre
    case "music-videos": self = .musicVideo
    case "playlists": self = .playlist
    case "radio-shows": self = .radioShow
    case "record-labels": self = .recordLabel
    case "songs": self = .song
    case "stations": self = .station
    case "tracks": self = .track
    default: return nil
    }
  }
}

extension MusicKitPlugin {
  class MusicPlayerStreamHandler: MusicKitPluginStreamHandler, FlutterStreamHandler {
    let musicPlayer: ApplicationMusicPlayer
    private var playerTask: Task<(), Never>?
    private var cancellable: AnyCancellable?
    
    init(musicPlayer: ApplicationMusicPlayer) {
      self.musicPlayer = musicPlayer
      super.init()
    }
    
    func onListen(withArguments arguments: Any?, eventSink events: @escaping FlutterEventSink) -> FlutterError? {
      eventSink = events
      
      cancellable = musicPlayer.state
        .objectWillChange
        .makeConnectable()
        .autoconnect()
        .sink { [weak self] in
          DispatchQueue.main.async { [weak self] in
            self?.eventSink?(self?.musicPlayer.state.jsonObject())
          }
        }
      
      return nil
    }
    
    func onCancel(withArguments arguments: Any?) -> FlutterError? {
      eventSink = nil
      playerTask?.cancel()
      playerTask = nil
      cancellable?.cancel()
      cancellable = nil
      return nil
    }
  }
}

extension MusicPlayer.State: JSONEncodable {
  enum CodingKeys: String, CodingKey {
    case playbackRate
    case playbackStatus
    case repeatMode
    case shuffleMode
  }
    
  public func encode(to encoder: Encoder) throws {
    var container = encoder.container(keyedBy: CodingKeys.self)
    try container.encode(playbackRate, forKey: .playbackRate)
    try container.encode(playbackStatus.intValue, forKey: .playbackStatus)
    try container.encode(repeatMode?.intValue, forKey: .repeatMode)
    try container.encode(shuffleMode?.intValue, forKey: .shuffleMode)
  }
}

extension MusicPlayer.PlaybackStatus {
  public var intValue: Int {
    switch self {
    case .stopped: return 0
    case .playing: return 1
    case .paused: return 2
    case .interrupted: return 3
    case .seekingBackward: return 4
    case .seekingForward: return 5
    
    @unknown default:
      return 0
    }
  }
}

extension MusicPlayer.RepeatMode {
  public var intValue: Int {
    switch self {
    case .none: return 0
    case .one: return 1
    case .all: return 2
    @unknown default:
      return 0
    }
  }
  
  init(_ intValue: Int) {
    switch intValue {
    case 0:
      self = .none
    case 1:
      self = .one
    case 2:
      self = .all
    default:
      self = .none
    }
  }
}

extension MusicPlayer.ShuffleMode {
  public var intValue: Int {
    switch self {
    case .off: return 0
    case .songs: return 1
    @unknown default:
      return 0
    }
  }
  
  init(_ intValue: Int) {
    switch intValue {
    case 0:
      self = .off
    case 1:
      self = .songs
    default:
      self = .off
    }
  }
}
